package com.xiaoyu.awakening.common.core.kit;


import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;

import java.lang.reflect.Field;
import java.util.TreeMap;

/**
 * 签名验签工具类
 *
 * <p>
 * A系统调用B系统
 * 1、双方约定ticket加密验证授权信息
 * 2、A系统加密code作为ticket，根据ticket对请求参数字典序做签名，向B系统传输ticket、签名数据、原始报文
 * 3、B系统接收到请求验证ticket，同样方式对接收的参数做签名然后验签，验证通过做业务处理，然后以同样的签名、验签方式响应数据
 * </p>
 *
 * @author wangxiaoyu
 * @since v1.0
 */
public class SignKit {

    /**
     * 公钥
     */
    private static final String PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDd5uBkfX5RFAuwQjrEq97DINR0c7cxbCcoOQUV/iiV2JtCmz3REZpmNmLZpWAsztf/epmjuFFeS29tpkEcB8ybCJV9Cc5nKQgx0G9SGt5pHTm6InNzVTY7WMFpjkP/2f3MkcPQhoYhn40a135SrB+VD/mBU8SvlXSYDdApD321JQIDAQAB";

    /**
     * 私钥
     */
    private static final String PRIVATE_KEY = "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAN3m4GR9flEUC7BCOsSr3sMg1HRztzFsJyg5BRX+KJXYm0KbPdERmmY2YtmlYCzO1/96maO4UV5Lb22mQRwHzJsIlX0JzmcpCDHQb1Ia3mkdOboic3NVNjtYwWmOQ//Z/cyRw9CGhiGfjRrXflKsH5UP+YFTxK+VdJgN0CkPfbUlAgMBAAECgYADak2YflihWtOgj4RkCS5726zUFgEty40BZiSZOrjZMDpDg4F8uZlMwBg0DVg2QwC5Yo5WzLwZMsQGRgFz3agye01mIS2rCnu5tyIeP9xYsp0etzgJu8PRe0RtsVnKM642ksIpPM3mXunjZF3oxpkcrK1AKumfjolXp5tRA6uqgQJBAPxpgKSZlP+u9LxRoxl1vrsfQCx37GwQhjG13CRSW00p5wcJ/PghjFUWk8YrPPTIRD81z/6MOrTGKsCtHg1mFDECQQDhDlnRCcnVD8FYLTzM+YdIxQgAygxKYcu4EpWXa7/s/1ETbCocZtKsXT07S1mbHPT+TFyFqjWOZBl5un0jFTc1AkEA2QZpep0m1HR8rxLOVoy7sq5bAiEWoY/PohxIrN427HDT2GSJIlvOCjcAVgpFPf6XV3YQYT/3FgmxJrtytHiJcQJBAIwgKFIcOxu6vJJPUEz7jH0FmK8NHkvtyi7o+1UVVKGWcn+aWDno14FUaHyTtM38YJo5J15eFQx8TNRbBfEjE4UCQBU1P2jyIPSmVQs0cvCQ38kKA6Vv3fAneD3HsDkuXQMCyuC1xMac4l1C9WbnFrBtzlKBW75krVgzILXcGN/ivZg=";

    /**
     * 签名
     *
     * @param params 参数
     * @author wangxiaoyu
     */
    public static String sign(Object params, String code) throws IllegalAccessException {
        String secret = getCodeBase64(code);
        StringBuffer signSbf = new StringBuffer();
        signSbf.append(secret).append(":");
        if (params instanceof String) {
            signSbf.append(params);
        } else {
            //获取参数字典序
            TreeMap<String, String> paramsMap = toTreeMap(params);
            //拼接
            paramsMap.forEach((k, v) -> {
                signSbf.append(k).append(v);
            });
        }
        signSbf.append(":").append(secret);
        //MD5
        byte[] signData = SecureUtil.md5().digest(signSbf.toString().getBytes());
        //BASE64
        return Base64.encode(signData);
    }

    /**
     * 验签
     *
     * @param params 参数
     * @param signed 签名
     * @author wangxiaoyu
     * @date 2022/7/26 20:45
     */
    public static Boolean verifySign(Object params, String signed, String code) throws IllegalAccessException {
        String sign = sign(params, code);
        return signed.equals(sign);
    }

    /**
     * 通过treeMap对字段排序
     *
     * @param obj 参数
     * @author wangxiaoyu
     * @date 2022/7/26 20:24
     */
    public static TreeMap<String, String> toTreeMap(Object obj) throws IllegalAccessException {
        if (obj == null) {
            return null;
        }
        TreeMap<String, String> result = new TreeMap<>();
        Class<?> objClazz = obj.getClass();
        for (Field declaredField : objClazz.getDeclaredFields()) {
            declaredField.setAccessible(true);
            String name = declaredField.getName();
            Object value = declaredField.get(obj);
            if (value != null && StrUtil.isNotBlank(value.toString())) {
                result.put(name, value.toString());
            }
        }
        return result;
    }

    /**
     * 生成AppIdBase64
     *
     * @author wangxiaoyu
     * @date 2022/7/27 11:15
     */
    public static String getCodeBase64(String code) {
        return Base64.encode(code);
    }

    /**
     * 生成ticket
     *
     * @author wangxiaoyu
     * @date 2022/7/27 14:18
     */
    public static String getTicket(String code) {
        // 公钥加密
        RSA pubRsa = new RSA(null, PUBLIC_KEY);
        return pubRsa.encryptBase64(code, KeyType.PublicKey);
    }

    public static String getCodeFromTicket(String ticket) {
        // 初始化RSA工具并设置私钥
        RSA priRsa = new RSA(PRIVATE_KEY, null);
        return priRsa.decryptStr(ticket, KeyType.PrivateKey);
    }
}
